#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "hashTable.h"

#define MAX_NAME 10


/** Structures **/


struct FileName {
  char * name; 
  struct FileName * next;
};


struct Cell {
	char*	key;
  struct FileName * names;
	struct PeerList * peers;
	struct Cell * next;
};

struct File {
	char* name;
	int 	length;
	int 	sizelength;
	char* key;
  struct File * next;
};

struct Peer {
	int 		port;
	char 		ip[4];
	struct  File * files;
};

struct PeerList {
	struct Peer * data;
	struct PeerList * next;
};

struct HashTable {
	struct Cell * head;
};

/** Fin Structures **/

/** Prototypes **/

void freePeer(struct Peer * p);
void freePeerList(struct PeerList * pl);
void freeFile(struct File * f);
void freeCell(struct Cell * c); 
void freeHashTable(struct HashTable * ht);

int isIn(struct PeerList * list, char ip[4]);

void addPeer(struct Cell * c, struct Peer * p); 
void addOrModifyCell(struct HashTable *, struct Cell*);

struct Cell * newCell(char * key); 

void deleteCell(struct HashTable *, struct Cell*);
void deletePeerInCell(struct Cell * c, struct Peer * p);

/** Fin Prototypes **/



int count = 0;

struct HashTable * newHashTable() {
	struct HashTable * ht = malloc(sizeof(struct HashTable));
	assert(ht!=NULL);
	ht->head = NULL;
	return ht;
}

struct Cell * newCell(char * key) {
	struct Cell * c = malloc(sizeof(struct Cell));
	assert(c!=NULL);
	c->key = strdup(key);

	return c;
}

struct PeerList * newPeer(struct PeerList * pl, int port, char * ip) {
	struct PeerList * p = malloc(sizeof(struct PeerList));
	p->data = malloc(sizeof(struct Peer));
	assert(p != NULL);
	assert(p->data != NULL);
	int j;
	char * delims = ".";
	char * ip_tmp = strdup(ip);

	p->data->ip[0] = atoi(strtok(ip_tmp, delims));
	for(j = 1; j < 4; j++)
		p->data->ip[j] = atoi(strtok(NULL, delims));

	free(ip_tmp);

	if(!isIn(pl,p->data->ip)) {
		p->data->port = port;
		p->data->files = NULL;
		p->next = pl;
		return p;
	}
	else {
		free(p);
		return pl;
	}
}

void addPeer(struct Cell * c, struct Peer * p) {
	assert(c!=NULL);
	if(p != NULL && !isIn(c->peers, p->ip)) {
		struct PeerList * newP = malloc(sizeof(struct PeerList));
    newP->data = p;
		newP->next = c->peers;
		c->peers = newP;
	}
}

int isIn(struct PeerList * cpeers, char ip[4]) {
  if(cpeers == NULL)
    return 0;
  else if(cpeers->data == NULL)
    return 0;

	struct PeerList * iterator = cpeers;
  assert(iterator != NULL);

	while(iterator != NULL) {
		if(!strcmp(iterator->data->ip, ip))
			return 1;
		else
			iterator = iterator->next;
	}
	return 0;
}

struct File * addFile(struct HashTable * ht, struct Peer * p, char * name, int length, int sizelength, char * key) {
	struct File * f = malloc(sizeof(struct File));
	assert(f != NULL);
	assert(ht != NULL);

	f->name = strdup(name);
	f->key = strdup(key);

	f->length = length;
	f->sizelength = sizelength;
	f->next = p->files;
	p->files = f;

	struct Cell * c = searchKey(ht, key);
	if(c == NULL) {
		c = newCell(key);

    c->names = malloc(sizeof(struct FileName));
    c->names->name = strdup(name);
    c->names->next = NULL;

    c->peers = malloc(sizeof(struct PeerList));
		addPeer(c, p);
		addOrModifyCell(ht, c);
	}
	else {
    struct FileName * n = malloc(sizeof(struct FileName));
    n->name = strdup(name);
    n->next = c->names;
    c->names = n;
		addPeer(c, p);
	}
	return f;
}

void freeFile(struct File * f) {
  if(f != NULL) {
    free(f->name);
    free(f->key);
    free(f);
  }
}

void freePeer(struct Peer * p) {
	if(p != NULL) {
		if(p->files != NULL) {
			struct File * tmp = NULL;
			do {
				tmp = p->files;
				p->files = p->files->next;
        freeFile(tmp);
			} while(p->files != NULL);
		}
		free(p);
	}
}

void freePeerList(struct PeerList * pl) {
	if(pl != NULL) {
		struct PeerList * tmp = NULL;
		do {
			tmp = pl;
			pl = pl->next;
			freePeer(tmp->data);
      free(tmp);
		} while(pl != NULL);
	}
}

void freeCell(struct Cell * c) {
	if(c != NULL) {
		if(c->peers != NULL) {
			do {
				struct PeerList * tmp = c->peers;
				c->peers = c->peers->next;
				free(tmp);
			} while(c->peers != NULL);
		}
    if(c->names != NULL) {
      do {
        struct FileName * n = c->names;
        c->names = c->names->next;
        free(n->name);
        free(n);
      } while(c->names != NULL);
    }
		free(c->key);
		free(c);
	}
}

void freeHashTable(struct HashTable * ht) {

	if(ht != NULL) {
		if(ht->head != NULL) {
			do{
				struct Cell * tmp = ht->head;
				ht->head = ht->head->next;
				freeCell(tmp);
			}	while(ht->head != NULL);
		}
		free(ht);
	}
}

void freeSystem(struct HashTable * ht, struct PeerList * pl) {
	freePeerList(pl);
	freeHashTable(ht);
}

void addOrModifyCell(struct HashTable * ht, struct Cell * c) {
	if(ht != NULL && ht->head != NULL && c != NULL) {
		struct Cell * tmp = searchKey(ht, c->key);
		if(tmp == NULL) {
			c->next = ht->head;
			ht->head = c;
		}
		else {
			c->next = tmp->next;
			tmp = c;
		}
	}
	else if( ht->head == NULL && c != NULL) {
		c->next = NULL;
		ht->head = c;
	}
}

void deletePeer(struct HashTable * ht, struct PeerList * pl, struct Peer * p) {
	if(ht != NULL && p != NULL) {
		struct File * iteratorF = p->files;
		struct Cell * c = searchKey(ht, iteratorF->key);
		while(c != NULL && iteratorF != NULL) {
			c = searchKey(ht, iteratorF->key);
			deletePeerInCell(c, p);
      iteratorF = iteratorF->next;
    }

		struct PeerList * iteratorP = pl;
		struct PeerList * iterator_old = NULL;
		while(iteratorP != NULL && strcmp(iteratorP->data->ip, p->ip)) {
			iterator_old = iteratorP;
			iteratorP = iteratorP->next;
		}

		if(iteratorP->next == NULL && iterator_old != NULL) {
			iterator_old->next = NULL;
		}
		else if(iterator_old == NULL) {
			pl = iteratorP->next;
		}
		else {
			iterator_old->next = iteratorP->next;
		}

		freePeer(p);
    free(iteratorP);
	}
}

void deletePeerInCell(struct Cell * c, struct Peer * p) {
	assert(c != NULL && p != NULL);
	struct PeerList * iterator = c->peers;
	struct PeerList * iterator_old = NULL;

	while(iterator != NULL && iterator->data != NULL && strcmp(iterator->data->ip, p->ip)) {
		iterator_old = iterator;
		iterator = iterator->next;
	}

	if(iterator->next == NULL && iterator_old != NULL) {
		iterator_old->next = NULL;
	}
	else if(iterator_old == NULL) {
		c->peers = iterator->next;
	}
	else {
		iterator_old->next = iterator->next;
	}
  free(iterator);
}

void deleteCell(struct HashTable * ht, struct Cell * c) {
	if(ht != NULL && ht->head != NULL && c != NULL) {
		struct Cell * iterator 		 = ht->head;
		struct Cell * iterator_old = NULL;

		while(iterator != NULL && strcmp(iterator->key, c->key)) {
			iterator_old = iterator;
			iterator = iterator->next;
		}

		if(iterator->next == NULL && iterator_old != NULL) {
			iterator_old->next = NULL;
		}
		else if(iterator_old == NULL) {
			ht->head = iterator->next;
		}
		else {
			iterator_old->next = iterator->next;
		}
		free(iterator);
	}
}

struct Peer * searchPeer(struct PeerList * pl, char * ip) {

	if(pl != NULL && pl->data != NULL) {
		char * delims = ".";
		char * ip_tmp = strdup(ip);
		char ip2[4];
		int j;
		
		ip2[0] = atoi(strtok(ip_tmp, delims));
		for(j = 1; j < 4; j++)
			ip2[j] = atoi(strtok(NULL, delims));

		free(ip_tmp);

    struct PeerList * iterator = pl;
    while(iterator->data != NULL && strcmp(iterator->data->ip, ip2))
      iterator = iterator->next;
    if(iterator == NULL)
      return NULL;
    else
      return iterator->data;
	}
}

struct Cell * searchKey(struct HashTable * ht, const char* key) {
	if(ht != NULL && ht->head != NULL) {
		struct Cell * iterator = ht->head;
		while(iterator != NULL && strcmp(iterator->key, key))
			iterator = iterator->next;
		if(iterator == NULL)
			return NULL;
		else
			return iterator;
	}
	else
		return NULL;
}

void displayHT(struct HashTable * ht) {
	count++;
	printf("%d affichage : \n", count);
	if(ht != NULL && ht->head != NULL) {
		struct Cell * iterator = ht->head;
		do {
			printf("Key %s \n", iterator->key);
      struct FileName * n = iterator->names;
      while(n != NULL) {
        printf("File Name = %s\n", n->name);
        n = n->next;
      }
      struct PeerList * p = iterator->peers;
      while(p != NULL) {
        printf("Peers = %p \n", p->data);
        p = p->next;
      }
			iterator = iterator->next;
		} while(iterator != NULL);
    printf("\n");
	}
}

void displayPL(struct PeerList * pl) {
	if(pl != NULL) {
		struct PeerList * p = pl;
		do {
			printf("Peer : %d.%d.%d.%d \n", p->data->ip[0], p->data->ip[1], p->data->ip[2], p->data->ip[3]);
			p = p->next;
		} while(p != NULL);
	}
	printf("\n");
}
